//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include "ddk.h"

#include "CWin32SocketService.h"
#define IncrementDllLockCount() do {} while(0)
#define DecrementDllLockCount() do {} while(0)

#include "_CWin32SocketService.cpp"

//#include "Winsock2.h"

#include "CWin32Socket.h"
#include <eltypes.h>
#include <stdio.h>

#include "Win32Socket/Win32Socket.h"

#define    WIN32_ENDPOINT_READABLE    ( 0x1 )
#define    WIN32_ENDPOINT_WRITEABLE   ( 0x2 )
#define    WIN32_ENDPOINT_EXCEPTIONAL ( 0x4 )

UInt32 CWin32SocketService::AddRef()
{
    UInt32 Refs=InterlockedIncrement(&m_cRef);
    return (UInt32)Refs;
}

UInt32 CWin32SocketService::Release()
{
    UInt32 Refs;
    Refs = (UInt32)InterlockedDecrement(&m_cRef);
    if (Refs == 0) {
        delete this;
    }
    return Refs;
}

extern int WIN32_WSAStartup(void);
extern int WIN32_WSACleanup();

ECode CWin32SocketService::constructor()
{
	  Int32 ret = WIN32_WSAStartup();
	  if (0 != ret) {
	      return E_INVALID_OPERATION;
	  }
	  return NOERROR;
}

CWin32SocketService::~CWin32SocketService()
{
    WIN32_WSACleanup();   
}

ELAPI _CProfile_GetAString( AString appName, AString keyName, AStringBuf *pReturnedString);

ECode CWin32SocketService::GetIPAddresses(
    /* [out] */ IPv4Address * pIpAddr,
    /* [out] */ IPv4Address * pNetmask,
    /* [out] */ IPv4Address * pGateway,
    /* [out] */ IPv4Address * pDns1,
    /* [out] */ IPv4Address * pDns2)
{
    AStringBuf_<32> ipBuf;

    assert(NULL != pIpAddr);
    assert(NULL != pNetmask);
    assert(NULL != pGateway);
    assert(NULL != pDns1);
    assert(NULL != pDns2);

    //only for IPV4
    _CProfile_GetAString("Tcpip", "LocalIP", &ipBuf);
    *((int *)pIpAddr) = WIN32_inet_addr(ipBuf);

    _CProfile_GetAString("Tcpip", "SubnetMask", &ipBuf);
    *((int *)pNetmask) = WIN32_inet_addr(ipBuf);

    _CProfile_GetAString("Tcpip", "DefaultGateway", &ipBuf);
    *((int *)pGateway) = WIN32_inet_addr(ipBuf);

    _CProfile_GetAString("Tcpip", "Nameserver", &ipBuf);
    *((int *)pDns1) = WIN32_inet_addr(ipBuf);

    return NOERROR;
}

ECode CWin32SocketService::CreateSocket(
    /* [in] */ Int32 family,
    /* [in] */ Int32 type,
    /* [in] */ Int32 protocol,
    /* [out] */ ISocket ** ppNewSocket,
    /* [out] */ Int32 * pError)
{
    win32_socket so;
    int ret = 0;
    CWin32Socket *pWin32Socket = NULL;

    ret = WIN32_socket(family, type, protocol, &so);
    if (0 != ret) {
    	*pError = ret;
        return S_FALSE;
    }

    pWin32Socket = new CWin32Socket(so);
    if (NULL == pWin32Socket) {
        return E_NOT_ENOUGH_MEMORY;
    }
    pWin32Socket->AddRef();
    *ppNewSocket = pWin32Socket;
    return NOERROR;
}

ECode CWin32SocketService::WaitForSockets(
    /* [in] */ const SocketTime & timeout,
    /* [in] */ const BufferOf<IInterface *> & sockets,
    /* [in] */ const MemoryBuf & eventRequests,
    /* [out] */ MemoryBuf* pEventResponses,
    /* [out] */ Int32* pError)
{
    win32_fd_set readfds, writefds, exceptfds;
    win32_timeval time;
    int numOfSocket = 0;
    int numOfSelectRet = 0;
    int ret = 0;
    int nfds = 0;
    int i = 0;

    assert(NULL != pEventResponses);
    assert(NULL != pError);

    WIN32_FD_ZERO(&readfds);
    WIN32_FD_ZERO(&writefds);
    WIN32_FD_ZERO(&exceptfds);

    numOfSocket = sockets.GetUsed();
    for (i = 0; i < numOfSocket; i++) {
        if (eventRequests[i] & WIN32_ENDPOINT_READABLE) {
            WIN32_FD_SET(((CWin32Socket *)sockets[i])->m_Socket, &readfds);
        }
        if (eventRequests[i] & WIN32_ENDPOINT_WRITEABLE) {
            WIN32_FD_SET(((CWin32Socket *)sockets[i])->m_Socket, &writefds);
        }
        if (eventRequests[i] & WIN32_ENDPOINT_EXCEPTIONAL) {
            WIN32_FD_SET(((CWin32Socket *)sockets[i])->m_Socket, &exceptfds);
        }
    }

    time.tv_sec = timeout.time_sec;
    time.tv_usec = timeout.time_usec;

    ret = WIN32_select(nfds, &readfds, &writefds, &exceptfds, &time, &numOfSelectRet);
    if (0 != ret) {
        *pError = ret;
        return S_FALSE;
    }

    pEventResponses->Copy(eventRequests.GetPayload(), numOfSocket);
    if (numOfSelectRet > 0) {
        for (i = 0; i < numOfSocket; i++) {
            if (WIN32_FD_ISSET(((CWin32Socket*)sockets[i])->m_Socket, &readfds)) {
                (*pEventResponses)[i] |= WIN32_ENDPOINT_READABLE << 3;
            }
            if (WIN32_FD_ISSET(((CWin32Socket*)sockets[i])->m_Socket, &writefds)) {
                (*pEventResponses)[i] |= WIN32_ENDPOINT_WRITEABLE << 3;
            }
            if (WIN32_FD_ISSET(((CWin32Socket*)sockets[i])->m_Socket, &exceptfds)) {
                (*pEventResponses)[i] |= WIN32_ENDPOINT_EXCEPTIONAL << 3;
            }
        }
    }
    if (0 == numOfSelectRet) {
        return S_TIMED_OUT;
    } 

    *pError = numOfSelectRet; 
    for (i = 0; i < numOfSocket; i++) {
        (*pEventResponses)[i] >>= 3;
    }
    return NOERROR;
}

